#pragma once
#include <vector>
#include <queue>
#include <pthread.h>
#include <unistd.h>
#include <mutex>
#include "Thread.hpp"
#include "LockGuard.hpp"
using namespace ThreadNs;
const int gnum = 5;

template <class T> // 声明
class ThreadPool;

template <class T>
struct ThreadData
{
    ThreadData(ThreadPool<T> *tp, const std::string &s)
        : _threadPool(tp), _name(s)
    {
    }
    ThreadPool<T> *_threadPool;
    std::string _name;
};
template <class T>
class ThreadPool
{
private:
    // 因为普通成员函数第一个参数是this指针，和回调方法不匹配，故改成static类型
    static void *handlerTask(void *args) // args是ThreadData对象指针
    {
        ThreadData<T> *td = static_cast<ThreadData<T> *>(args);
        while (1)
        {
            T t;
            {                                                  // RAII，出了作用域LockGuard会销毁，将析构锁
                LockGuard lockGuard(td->_threadPool->mutex()); // 加锁
                while (td->_threadPool->IsQueueEmpty())        // 如果队列为空，则等待
                {
                    td->_threadPool->ThreadWait();
                }
                // 线程能走到这里，说明队列一定有任务给线程
                t = td->_threadPool->Pop(); // 从队列中取出任务
            }
            t(); // Task的operator()
        }
        delete td; // 析构ThreadData对象
        return nullptr;
    }
    ThreadPool(const int &num = gnum)
        : _num(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
        // 创建线程
        for (int i = 0; i < _num; ++i)
        {
            _threads.push_back(new Thread());
        }
    }
    ThreadPool(const ThreadPool<T> &) = delete;               // 禁用拷贝构造
    ThreadPool<T> &operator=(const ThreadPool<T> &) = delete; // 禁用赋值运算符重载

public: // 解决静态handlerTask是静态函数的问题，这几个都是偷家函数
    void LockQueue() { pthread_mutex_lock(&_mutex); }
    void UnLockQueue() { pthread_mutex_unlock(&_mutex); }
    bool IsQueueEmpty() { return _taskQueue.empty(); }
    void ThreadWait() { pthread_cond_wait(&_cond, &_mutex); }
    T Pop()
    {
        T t = _taskQueue.front();
        _taskQueue.pop();
        return t;
    }
    pthread_mutex_t *mutex()
    {
        return &_mutex;
    }

public:
    void run() // 线程启动
    {
        for (const auto &t : _threads)
        {
            ThreadData<T> *td = new ThreadData<T>(this, t->threadName());
            t->start(handlerTask, (void *)td);
            std::cout << t->threadName() << "start..." << std::endl;
        }
    }
    void push(const T &in)
    {
        // RAII,出了作用域，锁将会被释放
        LockGuard lockGuard(&_mutex);
        _taskQueue.push(in);
        pthread_cond_signal(&_cond);
        std::cout << "任务发送成功" << std::endl;
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
        for (const auto &t : _threads)
        {
            delete t;
        }
    }
    static ThreadPool<T> *getInstance() // 这里的static的作用是让这个函数只有一份，获取单例对象。tp是临界资源，需要加锁
    {
        if (nullptr == tp) // 因为锁只创建一次，防止线程进来被锁阻塞
        {
            // 只进来一次就够了
            _singletonLock.lock();
            if (nullptr == tp) // 说明对象还没有被创建
            {
                tp = new ThreadPool<T>();
            }
            _singletonLock.unlock();
        }
        return tp;
    }

private:
    int _num;                       // 线程个数
    std::vector<Thread *> _threads; // 使用vector存放线程
    std::queue<T> _taskQueue;       // 任务队列，往里面放任务，它是共享资源，需要加锁保护
    pthread_mutex_t _mutex;         // 互斥锁
    pthread_cond_t _cond;           // 条件变量

    static ThreadPool<T> *tp;         // 单例模式静态的对象指针
    static std::mutex _singletonLock; // 获取单例对象使用的锁
};
template <class T>
ThreadPool<T> *ThreadPool<T>::tp = nullptr;

template <class T>
std::mutex ThreadPool<T>::_singletonLock;